import { Logger } from "../log/logger";
import Utils from "../utils/Utils";
import Pool from "../pool/Pool";
import { defaultSoundOption, getOption, SoundOption, SoundState } from "./SoundDefine";
import Tween from "../anim/Tween";
import PromiseReveal from "../utils/PromiseReveal";

export class SoundItem {

    static create(type: number) {
        let item = Pool.get(SoundItem);
        item._type = type;
        return item;
    }

    /** 0播放中 1暂停中 2已停止*/
    private _state: SoundState = SoundState.None;

    /**播放参数 */
    protected _opt: SoundOption;

    /**播放源 */
    protected _source: Laya.SoundChannel;

    protected _url: string;
    protected _type: number;

    /**基础音量（系统音量缩放*type音量缩放） */
    private _baseVolume: number = 100;

    /**音量缓动时临时值 */
    private _dynamicVolume: number = 100;

    private _pms: PromiseReveal;

    play(url: string, opt?: SoundOption) {

        let state = this._state;
        if (state === SoundState.Ready || state === SoundState.Play) {
            return;
        }

        if (state === SoundState.Pause) {
            this.resume();
            return;
        }

        if (Utils.isNil(url) || url.length <= 0) {
            return;
        }


        this._url = url;
        this._state = SoundState.Ready;
        this._pms = PromiseReveal.create();
        this._opt = getOption(opt, defaultSoundOption);

        try {
            this._source = this._play();
            this._state = SoundState.Play;
            this.fadeVolume(true);

        } catch (e) {
            this._state = SoundState.Stop;
            this._pms.reject();
            this.clear();
        }
    }

    /**暂停 */
    pause() {
        if (this._state === SoundState.Play) {
            this._state = SoundState.Pause;
            this._source.pause();
        }
    }

    /**继续 */
    resume() {
        if (this._state === SoundState.Pause) {
            this._state = SoundState.Play;
            this._source.resume();
        }
    }

    /**
     * 停止后会回收声音
     * 
     * @param slow 是否缓动
     */
    stop(slow: boolean = true) {

        if (this._state === SoundState.None || this._state === SoundState.Stop) {
            return;
        }

        Logger.logView(this._url, "声音结束：");

        this._state = SoundState.Stop;
        if (this._opt.fadeOut > 0 && slow) {
            this.fadeVolume(false, this.clear, this);
        } else {
            this.clear();
        }


    }

    /**
     * 声音结束
     * @returns 
     */
    wait() {
        if (this._state === SoundState.None || this._state === SoundState.Stop) {
            return Promise.resolve();
        }

        if (!this._pms) {
            return Promise.resolve();
        }

        return this._pms;
    }

    clear() {

        this._state = SoundState.None;

        Tween.clear(this);

        if (this._source) {
            this._source.stop();
            this._source = null;
        }

        if (this._pms) {
            this._pms.resolve();
            this._pms = null;
        }

        this._opt = this._source = this._url = this._type = null;

        this.recover();
    }


    fadeVolume(fadein: boolean, callBack?: Function, thisArg?: any) {

        Tween.clear(this);

        let v = 0, d = 0;
        if (fadein && this._opt.fadeIn > 0) {
            this._source.volume = 0;
            d = this._opt.fadeIn;
            v = this.volume;
        }

        if (!fadein && this._opt.fadeOut > 0) {
            this._source.volume = this.volume;
            d = this._opt.fadeOut;
            v = 0;
        }

        if (d === 0) {
            callBack && callBack.apply(thisArg);
            this.updateVolume();
        } else {
            Tween.get<SoundItem>(this, { loop: true }).to({ dynamicVolume: v }, d).call(callBack, thisArg);
        }
    }

    updateVolume() {
        if (!this._source) return;
        this._source.volume = this.volume;
    }

    resetVolumeDynamic() {
        this._dynamicVolume = 100;
        this.updateVolume();
    }

    recover() {
        Pool.put(this);
    }

    public get volume() {
        return Utils.lerpScale(this._baseVolume, 100, this._dynamicVolume, this._opt.volume) / 100
    }

    public set baseValume(v: number) {
        this._baseVolume = v;
        this.updateVolume();
    }

    public set dynamicVolume(v: number) {
        this._dynamicVolume = v;
        this.updateVolume();
    }

    public get url() {
        return this._url;
    }

    public get duration() {

        return this._source.duration
    }

    public get priority() {

        return this._opt.priority;
    }

    protected _play() {
        let opt = this._opt;
        let elm = Laya.SoundManager.playSound(this._url, opt.loop, Laya.Handler.create(this, this.onComplete), null, opt.start);
        return elm
    }

    protected onComplete() {
        if (this._state === SoundState.None) return;
        this.clear();
    }
}

export class soundMusic extends SoundItem {

    static create(): soundMusic {
        return Pool.get(soundMusic);
    }

    protected _play(): Laya.SoundChannel {
        let opt = this._opt;
        let elm = Laya.SoundManager.playMusic(this._url, opt.loop, Laya.Handler.create(this, this.onComplete), opt.start);
        return elm
    }
}